﻿//   BinaryConverter.cs
//
//   Copyright 2009 Eric Llewellyn
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace LibEELbeta
{
    public class BinaryConverter
    {
        public byte[] InstructionsToBinary(Instruction[] ins)
        {
            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);

            bw.Write((byte)24);

            foreach (Instruction i in ins)
            {
                bw.Write((byte)i.opCode);

                if (i.opCode == InstructionCodes.Push)
                {
                    if (i.data == null)
                    {
                        bw.Write((byte)BinaryDataTypes.Null);
                    }
                    else if (i.data is bool)
                    {
                        bw.Write((byte)BinaryDataTypes.Bool);
                        bw.Write((bool)i.data);
                    }
                    else if (i.data is double)
                    {
                        bw.Write((byte)BinaryDataTypes.Double);
                        bw.Write((double)i.data);
                    }
                    else if (i.data is string)
                    {
                        bw.Write((byte)BinaryDataTypes.String);
                        bw.Write((string)i.data);
                    }
                    else if (i.data is Dictionary<object, object>)
                    {
                        bw.Write((byte)BinaryDataTypes.Dictionary);
                    }
                }
                else if (i.opCode == InstructionCodes.Label)
                {
                    bw.Write((string)i.data);
                }
            }

            bw.Close();
            return ms.ToArray();
        }

        public Instruction[] BinaryToInstructions(byte[] prog)
        {
            BinaryReader br = new BinaryReader(new MemoryStream(prog));
            List<Instruction> ins = new List<Instruction>();
            InstructionCodes opCode;
            BinaryDataTypes dataType;

            br.ReadByte();

            while (br.BaseStream.Position < br.BaseStream.Length)
            {
                opCode = (InstructionCodes)br.ReadByte();

                if (opCode == InstructionCodes.Push)
                {
                    dataType = (BinaryDataTypes)br.ReadByte();
                    switch (dataType)
                    {
                        case BinaryDataTypes.Null:
                            ins.Add(new Instruction(opCode, null));
                            break;
                        case BinaryDataTypes.Bool:
                            ins.Add(new Instruction(opCode, br.ReadBoolean()));
                            break;
                        case BinaryDataTypes.Double:
                            ins.Add(new Instruction(opCode, br.ReadDouble()));
                            break;
                        case BinaryDataTypes.String:
                            ins.Add(new Instruction(opCode, br.ReadString()));
                            break;
                        case BinaryDataTypes.Dictionary:
                            ins.Add(new Instruction(opCode, new Dictionary<object, object>()));
                            break;
                    }
                }
                else if (opCode == InstructionCodes.Label)
                    ins.Add(new Instruction(opCode, br.ReadString()));
                else
                    ins.Add(new Instruction(opCode, null));
            }
            br.Close();

            return ins.ToArray();
        }
    }
}
